package top.go2do.websiteNavigation.business.website.service;

import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.Predicate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import top.go2do.common.dao.ApplicationRepository;
import top.go2do.common.dao.CategoryRepository;
import top.go2do.common.dao.TagRepository;
import top.go2do.common.dao.UserRepository;
import top.go2do.common.module.CategoryPO;
import top.go2do.common.module.TagPO;
import top.go2do.common.module.TagSimplyVO;
import top.go2do.common.module.UserPO;
import top.go2do.common.module.pageQuery.PageResult;
import top.go2do.common.utils.IdUtils;
import top.go2do.websiteNavigation.business.website.dao.WebsiteRepository;
import top.go2do.websiteNavigation.business.website.dao.WebsiteTagRepository;
import top.go2do.websiteNavigation.business.website.module.*;
import top.go2do.websiteNavigation.exception.WebsiteNavigationErrorCode;
import top.go2do.websiteNavigation.exception.WebsiteNavigationException;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Description
 * @Author xingyj
 * @CreateTime 2025/6/18 13:07
 */
@Service
public class WebsiteServiceImpl implements WebsiteService {
    @Autowired
    private WebsiteRepository websiteRepository;

    @Autowired
    private IdUtils idUtils;

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private WebsiteTagRepository websiteTagRepository;

    @Autowired
    private CategoryRepository categoryRepository;

    @Autowired
    private TagRepository tagRepository;

    @Override
    public WebsitePO create(WebsiteCreateForm createForm) {

        WebsitePO websitePO = new WebsitePO();
        BeanUtils.copyProperties(createForm, websitePO);
        Long websiteId = idUtils.getSnowflakeId();
        Long userId = createForm.getUserId();

        websitePO.setWebsiteId(websiteId);
        websitePO.setCreatedBy(userId);
        websitePO.setUpdatedBy(userId);
        websiteRepository.save(websitePO);
        return this.getById(websiteId);
    }

    @Override
    public void modifyById(WebsiteModifyForm modifyForm) {
        Long websiteId = modifyForm.getWebsiteId();
        WebsitePO websitePO = this.getById(websiteId);
//        String url = modifyForm.getUrl();
//        String title = modifyForm.getTitle();
//        String iconUrl = modifyForm.getIconUrl();
//        Boolean isPublic = modifyForm.getIsPublic();
//        Long categoryId = modifyForm.getCategoryId();
//        String description = modifyForm.getDescription();
//        private Integer sort;
//        private String jsonConfig;
//        private Integer status;
        BeanUtils.copyProperties(modifyForm, websitePO);
        websitePO.setUpdatedBy(modifyForm.getUserId());
        websiteRepository.save(websitePO);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void removeById(Long id) {
        this.getById(id);
        websiteRepository.deleteById(id);
        websiteTagRepository.deleteByWebsiteId(id);
    }

    @Override
    public WebsitePO getById(Long id) {
        WebsitePO websitePO = websiteRepository.findById(id).orElse(null);
        if (websitePO == null) {
            throw new WebsiteNavigationException(WebsiteNavigationErrorCode.GOODS_ID_NOT_EXIST);
        }
        return websitePO;
    }

    @Override
    public PageResult<WebsitePO> pageQuery(WebsiteQueryCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        /**
         * pageNumber 从0开始
         */
        Integer page = condition.getPage() - 1;
        Integer size = condition.getSize();
        Pageable pageable = PageRequest.of(page, size, sort);
        Specification<WebsitePO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getUserId() != null) {
                predicates.add(cb.equal(root.get("createdBy"), condition.getUserId()));
            }
            if (condition.getCategoryId() != null) {
                predicates.add(cb.equal(root.get("categoryId"), condition.getCategoryId()));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("title"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("description"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            String tagIds = condition.getTagIds();
            if(StringUtils.hasText(tagIds)) {
                Set<Long> tagIdSet = Arrays.stream(tagIds.split(","))
                        .map(Long::parseLong)
                        .collect(Collectors.toSet());
                List<WebsiteTagPO> websiteTagPOS = websiteTagRepository.findByTagIdIn(tagIdSet);
                // 提取与标签关联的所有 websiteId
                Set<Long> websiteIds = websiteTagPOS.stream()
                        .map(WebsiteTagPO::getWebsiteId)
                        .collect(Collectors.toSet());
                // 构造 IN 查询条件
                CriteriaBuilder.In<Long> in = cb.in(root.get("websiteId"));
                for (Long id : websiteIds) {
                    in.value(id);
                }
                predicates.add(in);
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        Page<WebsitePO> queryResult = websiteRepository.findAll(spec, pageable);
        PageResult<WebsitePO> pageResult = new PageResult(queryResult.getNumber(), queryResult.getSize(), queryResult.getTotalElements(), queryResult.getTotalPages());
        pageResult.setList(queryResult.getContent());
        return pageResult;
    }

    @Override
    public List<WebsitePO> listAll(WebsiteQueryAllCondition condition) {
        Sort.Direction direction = Sort.Direction.fromString(condition.getDirection());
        Sort sort = Sort.by(direction, condition.getOrderBy());

        Specification<WebsitePO> spec = (root, query, cb) -> {
            List<Predicate> predicates = new ArrayList<>();
            if (condition.getUserId() != null) {
                predicates.add(cb.equal(root.get("createdBy"), condition.getUserId()));
            }
            if (condition.getCategoryId() != null) {
                predicates.add(cb.equal(root.get("categoryId"), condition.getCategoryId()));
            }

            if (condition.getKeyword() != null) {
                Predicate appIdMatch = cb.like(root.get("title"), "%" + condition.getKeyword() + "%");
                Predicate appNameMatch = cb.like(root.get("description"), "%" + condition.getKeyword() + "%");
                predicates.add(cb.or(appIdMatch, appNameMatch)); // 核心修改点
            }
            return cb.and(predicates.toArray(new Predicate[0]));
        };
        return websiteRepository.findAll(spec, sort);
    }

    @Override
    public List<WebsiteListVO> po2vo(List<WebsitePO> list) {
        Set<Long> userIdSet = new HashSet<>();
        Set<Long> websiteIdSet = new HashSet<>();
        Set<Long> categoryIdSet = new HashSet<>();

        for (WebsitePO po : list) {
            userIdSet.add(po.getCreatedBy());
            userIdSet.add(po.getUpdatedBy());
            websiteIdSet.add(po.getWebsiteId());
            categoryIdSet.add(po.getCategoryId());
        }
        List<UserPO> userList = userRepository.findAllById(userIdSet);
        List<WebsiteTagPO> websiteTagList = websiteTagRepository.findByWebsiteIdIn(websiteIdSet);
        List<CategoryPO> categoryList = categoryRepository.findAllById(categoryIdSet);


        Set<Long> tagIdSet;
        if (websiteTagList != null) {
            tagIdSet = websiteTagList.stream().map(WebsiteTagPO::getTagId).collect(Collectors.toSet());
        } else {
            tagIdSet = new HashSet<>();
        }
        List<TagPO> tagList = tagRepository.findAllById(tagIdSet);

        Map<Long, String> userId2NameMap;
        Map<Long, TagPO> tagId2POMap;
        Map<Long, String> categoryId2NameMap;
        Map<Long, Set<TagSimplyVO>> websiteId2TagListMap;


        if (userList != null) {
            userId2NameMap = userList.stream().collect(Collectors.toMap(UserPO::getUserId, UserPO::getUsername));
        } else {
            userId2NameMap = new HashMap<>(0);
        }
        if (categoryList != null) {
            categoryId2NameMap = categoryList.stream().collect(Collectors.toMap(CategoryPO::getCategoryId, po -> po.getCategoryName()));
        } else {
            categoryId2NameMap = new HashMap<>(0);
        }
        if (tagList != null) {
            tagId2POMap = tagList.stream().collect(Collectors.toMap(TagPO::getTagId, po -> po));
        } else {
            tagId2POMap = new HashMap<>(0);
        }

        if (websiteTagList != null) {
            websiteId2TagListMap = new HashMap<>();
            for (WebsiteTagPO websiteTagPO : websiteTagList) {
                Long websiteId = websiteTagPO.getWebsiteId();
                Long tagId = websiteTagPO.getTagId();
                Set<TagSimplyVO> tagSet = websiteId2TagListMap.get(websiteId);
                if (tagSet == null) {
                    tagSet = new HashSet<>();
                }
                TagPO po = tagId2POMap.get(tagId);
                // 兼容标签被删除的情况
                if (po != null) {
                    TagSimplyVO tagVo = new TagSimplyVO();
                    tagVo.setTagId(String.valueOf(po.getTagId()));
                    tagVo.setTagName(po.getTagName());
                    tagSet.add(tagVo);
                    websiteId2TagListMap.put(websiteId, tagSet);
                }
            }
        } else {
            websiteId2TagListMap = new HashMap<>(0);
        }

        List<WebsiteListVO> voList = list.stream().map(po -> {
            WebsiteListVO vo = new WebsiteListVO();
            BeanUtils.copyProperties(po, vo);
            vo.setCategoryId(String.valueOf(po.getCategoryId()));
            vo.setCategoryName(categoryId2NameMap.get(po.getCategoryId()));
            vo.setTagList(websiteId2TagListMap.get(po.getWebsiteId()));
            vo.setCreatedByName(userId2NameMap.get(po.getCreatedBy()));
            vo.setUpdatedByName(userId2NameMap.get(po.getUpdatedBy()));
            vo.setWebsiteId(String.valueOf(po.getWebsiteId()));
            vo.setUpdatedBy(String.valueOf(po.getUpdatedBy()));
            vo.setCreatedBy(String.valueOf(po.getCreatedBy()));
            return vo;
        }).toList();
        return voList;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public void setTags(WebsiteTagsForm form) {
        Long websiteId = form.getWebsiteId();
        WebsitePO po = this.getById(websiteId);
        po.setUpdatedBy(form.getUserId());
        websiteRepository.save(po);
        /**
         * step: 先删除所有，再插入最新
         */
        websiteTagRepository.deleteByWebsiteId(websiteId);
        List<WebsiteTagPO> rpList = new ArrayList<>();
        form.getTagIdList().forEach(tagId -> {
            WebsiteTagPO websiteTagPO = new WebsiteTagPO();
            websiteTagPO.setWebsiteId(websiteId);
            websiteTagPO.setTagId(tagId);
            rpList.add(websiteTagPO);
        });
        websiteTagRepository.saveAll(rpList);
    }
}
